<?php

namespace app\shop\job;

use app\shop\library\ShopHelper;
use app\shop\library\ShopLogHelper;
use think\facade\Cache;
use think\queue\Job;
use Throwable;

/**
 * 队列任务执行类名
 * @property string $className
 */
#[\AllowDynamicProperties]  //兼容php8.2弃用动态属性创建
class BaseJob
{
    // 最大执行次数
    protected int $maxAttempts = 3;

    // 重试延迟执行时间
    protected int $delay = 10;

    // 队列名称
    protected static string $queue = '';

    public function __construct()
    {
        //获取执行类名
        $this->className = get_class($this);
    }

    protected function start(Job $job, $data): void
    {
        $attempts = $job->attempts();

        if ($attempts > $this->maxAttempts) {
            $this->failed($data);
            $job->delete();
        }

        $this->addJobLog("队列任务【 $this->className 】开始执行, 执行次数：" . $attempts, $data);
    }

    public function addJobLog(string $message, array|string $data = ''): void
    {
        ShopLogHelper::info($message, $data);
    }

    /**
     * 任务无需执行
     * @param Job   $job
     * @param mixed $data
     */
    public function noNeedJob(Job $job, mixed $data): void
    {
        if (is_array($data)) {
            $data = ShopHelper::array_to_json($data);
        }
        ShopLogHelper::info("队列任务【 $this->className 】无需执行", $data);

        $job->delete();
    }

    /**
     * 任务执行成功
     * @param Job $job
     * @return void
     */
    public function successJob(Job $job): void
    {
        ShopLogHelper::info("队列任务【 $this->className 】执行完毕");

        $job->delete();
    }

    /**
     * 任务执行异常
     * @param Job            $job
     * @param mixed          $data
     * @param Throwable|null $e
     * @return void
     */
    public function exceptionJob(Job $job, mixed $data, Throwable $e = null): void
    {
        ShopLogHelper::error("队列任务【 $this->className 】执行异常", [
            'data'    => $data,
            'attempt' => $job->attempts(),
        ], $e);

        $attempts = $job->attempts();
        if ($attempts >= $this->maxAttempts) {
            $this->failed($data, $e);
            $job->delete();
        } else {
            $job->release($this->delay);
        }
    }

    /**
     * 任务执行失败
     * @param array          $data
     * @param Throwable|null $e
     * @return void
     */
    public function failed(mixed $data = [], Throwable $e = null): void
    {
        ShopLogHelper::error("队列任务【 $this->className 】执行失败");

        $errInfo = [
            'job' => $this->className,
        ];
        if (is_array($data)) {
            $errInfo = array_merge($errInfo, $data);
        } else {
            $errInfo = array_merge($errInfo, ['data' => $data]);
        }
        if ($e) {
            $errInfo['err'] = [
                'msg'  => $e->getMessage(),
                'line' => $e->getLine(),
                'file' => $e->getFile()
            ];
        }

        if (static::$queue) {
            $queueName = static::$queue;
        } else {
            $queueName = basename($this->className);
        }

        $select = config('queue.connections.redis.select');
        Cache::store('redis')->handler()->select($select);
        Cache::store('redis')->handler()->zadd("{queues:failed}:$queueName", time(), ShopHelper::array_to_json($errInfo));
    }
}